#ifndef YFWZ100_FUNDAMENTAL_LINKEDLIST_HPP
#define YFWZ100_FUNDAMENTAL_LINKEDLIST_HPP

#include "common.hpp"

YFWZ100_FUNDAMENTAL_NS_BEGIN

template <typename T>
class LinkedList
{
private:
  // the node of the linked list.
  struct node
  {
    node *prev, *next;
    T value;
    node(T val) : value(val), prev(nullptr), next(nullptr) {}
    friend class iterator;
  } * _head, *_tail;
  // the size.
  size_t _size;

public:
  LinkedList() : _head(nullptr), _tail(nullptr), _size(0) {}

  virtual ~LinkedList()
  {
    this->clear();
  }

  void clear()
  {
    while (_head != nullptr)
    {
      node *n = _head;
      _head = _head->next;
      delete n;

      _size -= 1;
    }
  }

  void push_back(T value)
  {
    if (_head == nullptr)
    {
      _head = _tail = new node(value);
    }
    else
    {
      node *p = new node(value);
      _tail->next = p;
      p->prev = _tail;
      _tail = p;
    }
    _size += 1;
  }

  void remove_by_index(int index)
  {
    node *t = _head;
    while (index-- > 0)
    {
      t = t->next;
    }
    node *prev = t->prev;
    node *next = t->next;
    prev->next = next;
    next->prev = prev;
    delete t;
  }

  void remove_by_value(const T &value)
  {
    for (node *t = _head; t != nullptr; t = t->next)
    {
      if (t->value == value)
      {
        node *prev = t->prev;
        node *next = t->next;
        prev->next = next;
        next->prev = prev;
        delete t;
      }
    }
  }

  T &head() const
  {
    return *_head;
  }

  T &tail() const
  {
    return *_tail;
  }

  class iterator
  {
  private:
    node *_current;

  public:
    iterator(node *n) : _current(n) {}

    iterator &operator++()
    {
      _current = _current->next;
      return *this;
    }

    iterator operator++(int)
    {
      node *n = _current;
      // move forward.
      _current = _current->next;
      // return the temporary pointer instead.
      return iterator(n);
    }

    iterator operator+(int i)
    {
      node *n = _current;
      while (i-- > 0)
      {
        n = n->next;
      }
      return iterator(n);
    }

    iterator operator-(int i)
    {
      node *n = _current;
      while (i-- > 0)
      {
        n = n->prev;
      }
      return iterator(n);
    }

    T &operator*() const
    {
      return _current->value;
    }

    bool operator==(const iterator &right) const
    {
      return _current == right._current;
    }

    bool operator!=(const iterator &right) const
    {
      return _current != right._current;
    }

    T operator[](size_t index)
    {
      return *(this + index);
    }

    // delete current node and move forward.
    void drop_and_forward()
    {
      node *prev = _current->prev;
      node *next = _current->next;
      prev->next = next;
      next->prev = prev;
      delete _current;
      _current = next;
    }
  };

  T &operator[](int index)
  {
    return *(this->begin() + index);
  }

  size_t size() const
  {
    return _size;
  }

  bool isEmpty() const
  {
    return _head == nullptr;
  }

  iterator begin()
  {
    return iterator(_head);
  }

  iterator end()
  {
    return iterator(_tail->next);
  }
};

YFWZ100_FUNDAMENTAL_NS_END

#endif //YFWZ100_FUNDAMENTAL_LINKEDLIST_HPP